#include "simple/geom/iterator.hpp"
#include "simple/geom/algorithm.hpp"

#include <cassert>
#include <memory>
#include <chrono>
#include <iostream>

// #include <boost/gil.hpp>

using namespace simple::geom;
using namespace std::chrono;

#undef assert
#define assert(x) if(not (x)) std::puts("aaaa!");

template <unsigned width, unsigned height, unsigned step>
void BasicReadWrite()
{
	constexpr vector size{width,height};
	constexpr unsigned pitch = size.x() + step;
	auto image_ptr = std::make_unique<std::array<unsigned, pitch * size.y()>>();
	auto& image = *image_ptr;

	loop(size, [&, position = unsigned()] (auto i) mutable
	{
		image[i.y() * pitch + i.x()] = position;
		++position;
	});

	std::cout << "generic" << '\n';
	{
		auto now = high_resolution_clock::now();
		iterator begin{image.begin(), 0u};
		iterator end{image.end()-step, size.x()};
		unsigned i = 0;
		while(true)
		{
			auto done = begin == end;
			if(done) break;
			if(done.first_false != 0)
				begin.next(step,done);
			assert(*begin == i);
			++begin;
			++i;
		}
		assert(i == size.x() * size.y());
		std::cout << (high_resolution_clock::now() - now).count() << '\n';
	}

	std::cout << "generic vectirator" << '\n';
	{
		auto now = high_resolution_clock::now();
		vectirator begin{image.begin(), {0u, 0u}};
		vectirator end{image.begin(), {size.x(), size.y()*pitch - step}};
		unsigned i = 0;
		while(true)
		{
			auto done = begin == end;
			if(done) break;
			if(to_disjunction(done))
				begin.next({1u, step}, done);
			assert(*begin == i);
			++begin;
			++i;
		}
		assert(i == size.x() * size.y());
		std::cout << (high_resolution_clock::now() - now).count() << '\n';
	}

	// std::cout << "generic nested" << '\n';
	// {
	// 	auto now = high_resolution_clock::now();
	// 	iterator begin{image.begin(), 0u};
	// 	iterator end{image.end()-step, size.x()};
	// 	unsigned i = 0;
	// 	while(true)
	// 	{
	// 		auto done = begin == end;
	// 		if(done) break;
	// 		// if(to_disjunction(done))
	// 		if(done.first_false != 0)
	// 			begin.next(step,done);
	// 		while(begin != end)
	// 		{
	// 			assert(*begin == i);
	// 			++begin;
	// 			++i;
	// 		};
	// 	}
	// 	assert(i == size.x() * size.y());
	// 	std::cout << (high_resolution_clock::now() - now).count() << '\n';
	// }

	// std::cout << "indexing" << '\n';
	// {
	// 	auto now = high_resolution_clock::now();
	// 	unsigned j = 0;
	// 	loop(size, [&](auto i)
	// 	{
	// 		auto index = i.y() * pitch + i.x();
	// 		assert(image[index] == j);
	// 		j++;
	// 	});
	// 	assert(j == size.x() * size.y());
	// 	std::cout << (high_resolution_clock::now() - now).count() << '\n';
	// }

	// std::cout << "boost gil index" << '\n';
	// {
	// 	auto now = high_resolution_clock::now();
	// 	auto view = boost::gil::interleaved_view(size.x(), size.y(), image.begin(), pitch * sizeof(decltype(*image.begin())));
	// 	unsigned i = 0;
	// 	for(int y = 0; y < view.height(); ++y)
	// 	{
	// 		for(int x = 0; x < view.width(); ++x)
	// 		{
	// 			assert(i == view(x,y));
	// 			++i;
	// 		}
	// 	}
	// 	assert(i == size.x() * size.y());
	// 	std::cout << (high_resolution_clock::now() - now).count() << '\n';
	// }

	// std::cout << "boost gil iterator" << '\n';
	// {
	// 	auto now = high_resolution_clock::now();
	// 	auto view = boost::gil::interleaved_view(size.x(), size.y(), image.begin(), pitch * sizeof(decltype(*image.begin())));
	// 	unsigned i = 0;
	// 	for(int y = 0; y < view.height(); ++y)
	// 	{
	// 		auto it = view.row_begin(y);
	// 		for(int x = 0; x < view.width(); ++x)
	// 		{
	// 			assert(i == *it);
	// 			++it;
	// 			++i;
	// 		}
	// 	}
	// 	assert(i == size.x() * size.y());
	// 	std::cout << (high_resolution_clock::now() - now).count() << '\n';
	// }

	// std::cout << "hand rolled" << '\n';
	// {
	// 	auto now = high_resolution_clock::now();
	// 	auto begin = image.begin();
	// 	unsigned i = 0;
	// 	for(unsigned y = 0; y != size.y(); ++y)
	// 	{
	// 		std::for_each(begin, begin + size.x(), [&i](auto x)
	// 		{
	// 			assert(x == i);
	// 			++i;
	// 		});
	// 		begin += pitch;
	// 	}
	// 	assert(i == size.x() * size.y());
	// 	std::cout << (high_resolution_clock::now() - now).count() << '\n';
	// }

	// std::cout << "hand rolled index" << '\n';
	// {
	// 	auto now = high_resolution_clock::now();
	// 	unsigned i = 0;
	// 	for(int y = 0; y != (int)size.y(); ++y)
	// 	{
	// 		for(int x = 0; x != (int)size.x(); ++x)
	// 		{
	// 			auto index = y * pitch + x;
	// 			assert(i == image[index]);
	// 			++i;
	// 		}
	// 	}
	// 	assert(i == size.x() * size.y());
	// 	std::cout << (high_resolution_clock::now() - now).count() << '\n';
	// }

	std::cout << "--------------" << '\n';
}

int main()
{
	// return 0;
	BasicReadWrite<1,2,3>();
	BasicReadWrite<1,1,1>();
	BasicReadWrite<1,2,0>();
	BasicReadWrite<1,1,0>();
	// TODO: figure out what's wrong with these and if needs fixing
	// BasicReadWrite<1,0,3>();
	// BasicReadWrite<1,0,0>();
	BasicReadWrite<0,0,0>();
	BasicReadWrite<1000,1000,200>();
	BasicReadWrite<2000,2000,4000>();
	BasicReadWrite<1000,1000,0>();
	BasicReadWrite<2000,2000,0>();
	// BasicReadWrite<20000,20000,0>();
}
